package com.bc.plugin.doc.resolver.help;

import lombok.ToString;

import java.lang.reflect.Field;
import java.lang.reflect.TypeVariable;
import java.util.*;
/**
 * @author xhh
 */
public class JavaFieldHelper {

    private static Map<String, FieldTypeModel> cacheMap = new HashMap<>();

    public static Map<String, String> getFieldTypeMap(String className) {
        if (className.startsWith("java.util") || !className.contains("<")) {
            return null;
        }
        String simpleClassName = className.substring(0, className.indexOf("<"));
        FieldTypeModel fieldTypeModel = cacheMap.get(simpleClassName);
        if (fieldTypeModel == null) {
            fieldTypeModel = new FieldTypeModel(simpleClassName);
            cacheMap.put(fieldTypeModel.simpleClassName, fieldTypeModel);
        }
        Map<String, String> fieldTypeMap = new HashMap<>();
        List<String> fieldTypeList = JavaClassUtil.getGenericTypes(className);
        if (fieldTypeList == null) {
            return fieldTypeMap;
        }
        int size = fieldTypeList.size();
        for (FieldTypeTemplate fieldTypeTemplate : fieldTypeModel.fieldTypeTemplates) {
            if (fieldTypeTemplate.index >= size) {
                System.err.println("fieldTypeModel=" + fieldTypeModel);
                System.err.println("fieldTypeTemplate=" + fieldTypeTemplate);
                System.err.println("fieldTypeList size " + fieldTypeList.size() + " ," + fieldTypeList);
                throw new RuntimeException(className + " to fieldTypeList error");
            }
            String genericType = fieldTypeList.get(fieldTypeTemplate.index);
            if (fieldTypeTemplate.template == null) {
                fieldTypeMap.put(fieldTypeTemplate.fieldName, genericType);
            } else {
                fieldTypeMap.put(fieldTypeTemplate.fieldName, String.format(fieldTypeTemplate.template, genericType));
            }

        }

        return fieldTypeMap;
    }


    @ToString
    private static class FieldTypeModel {
        private List<FieldTypeTemplate> fieldTypeTemplates = new ArrayList<>();
        private String simpleClassName;

        private FieldTypeModel(String simpleClassName) {
            this.simpleClassName = simpleClassName;
            try {
                Class clazz = Class.forName(simpleClassName);
                TypeVariable[] typeVariables = clazz.getTypeParameters();
                List<Field> fieldList = new ArrayList<>();
                Class tempClass = clazz;
                while (tempClass != Object.class && tempClass != null) {
                    fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
                    tempClass = tempClass.getSuperclass();
                }
                for (Field field : fieldList) {
                    String typeName = field.getGenericType().getTypeName().replace(" ", "");
                    boolean isVariableType = false;
                    String template = null;
                    int index = 0;
                    for (int i = 0; i < typeVariables.length; i++) {
                        index = i;
                        TypeVariable typeVariable = typeVariables[i];
                        String matchStr = "<" + typeVariable.getTypeName() + ">";
                        String leftMatchStr = "<" + typeVariable.getTypeName() + ",";
                        String rightMatchStr = "," + typeVariable.getTypeName() + ">";
                        if (typeName.equals(typeVariable.getTypeName())) {
                            isVariableType = true;
                            break;
                        } else if (typeName.contains(matchStr)) {
                            isVariableType = true;
                            template = typeName.replace(matchStr, "<%s>");
                            break;
                        } else if (typeName.contains(leftMatchStr)) {
                            isVariableType = true;
                            template = typeName.replace(leftMatchStr, "<%s,");
                            break;
                        } else if (typeName.contains(rightMatchStr)) {
                            isVariableType = true;
                            template = typeName.replace(rightMatchStr, ",%s>");
                            break;
                        }
                    }
                    if (isVariableType) {
                        FieldTypeTemplate fieldTypeTemplate = new FieldTypeTemplate();
                        fieldTypeTemplate.fieldName = field.getName();
                        fieldTypeTemplate.index = index;
                        fieldTypeTemplate.template = template;
                        fieldTypeTemplates.add(fieldTypeTemplate);
                    }
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("ClassNotFoundException: " + simpleClassName);
            }

        }
    }

    @ToString
    private static class FieldTypeTemplate {
        private int index;
        private String fieldName;
        private String template;
    }

}
